//Copyright (C) 2007 James Ashley (www.imaginativeuniversal.com) under the terms of the LGPL.
using System;
using System.Collections.Generic;
using System.Text;
using System.Speech.Recognition;
using System.Speech.Synthesis;
using AIMLbot;
using System.Threading;
using System.IO;
using System.Xml;
using System.Drawing;


namespace SophiaBot
{
    public class SAPIBot
    {
        #region local fields

        protected SpeechRecognitionEngine _recognizer = null;
        protected bool _textToSpeechOn = false;
        protected bool _speechRecognitionOn = false;
        protected bool _specializedLexiconOn = false;
        protected bool _dictationOn = false;
        protected SpeechCommandBot _firstBot = null;
        protected IBotServer _currentBot = null;
        protected PlayGameTransitionDialogBot _dialogEngine = null;
        protected List<IBotServer> _botServers = new List<IBotServer>();
        protected VoiceBot _voiceBot = null;
        protected AIMLBotAdapter _aimlEngine = null;
        protected bool _isGrammarInitializationComplete = false;
        protected object grammarLock = new object();
        protected object speechLock = new object();
        protected long speechRecOnOffState = 0;
        protected object speechRecOnOffLock = new object();

        protected int recCount = 0;

        #endregion

        # region events

        public event GenericEventHandler<bool> TextToSpeechStateChanged;
        public event GenericEventHandler<bool> DictationStateChanged;
        public event GenericEventHandler<bool> SpecializedLexiconStateChanged;
        public event GenericEventHandler<string> WriteToOutput;
        public event GenericEventHandler<string> GuessText;
        public event GenericEventHandler<string> RejectText;
        public event GenericEventHandler<string> RecognizeText;
        public event EventHandler InputProcessed;
        public event GenericEventHandler<Color> ChangeFontColor;
        public event GenericEventHandler<Color> ChangeBackgroundColor;
        public event GenericEventHandler<string> ChangeFont;
        public event GenericEventHandler<string> UpdateStatus;

        #endregion

        #region public accessors

        /// <summary>
        /// Gets or sets a value indicating whether text to speech is on.
        /// </summary>
        /// <value><c>true</c> if text to speech on; otherwise, <c>false</c>.</value>
        public bool TextToSpeechOn
        {
            get { return _textToSpeechOn; }
            protected set 
            {
                _textToSpeechOn = value;
                _voiceBot.IsSpeechOn = _textToSpeechOn;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether speech recognition is on.
        /// </summary>
        /// <value><c>true</c> if speech recognition on; otherwise, <c>false</c>.</value>
        public bool SpeechRecognitionOn
        {
            get { return _speechRecognitionOn; }
            protected set { _speechRecognitionOn = value; 
            BotServerBase.DebugWriteToConsole("_speechRecognitionOn value changed to " + value.ToString()); }
        }

        /// <summary>
        /// Gets or sets a value indicating whether dictation is on.
        /// </summary>
        /// <value><c>true</c> if dictation on; otherwise, <c>false</c>.</value>
        public bool DictationOn
        {
            get { return _dictationOn; }
            protected set { _dictationOn = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether AIML lexicon is on.
        /// </summary>
        /// <value><c>true</c> if AIML lexicon on; otherwise, <c>false</c>.</value>
        public bool SpecializedLexiconOn
        {
            get { return _specializedLexiconOn; }
            protected set { _specializedLexiconOn = value; }
        }

        /// <summary>
        /// Gets the about.
        /// </summary>
        /// <value>The about.</value>
        public string About
        {
            get { return ReadTextFile("About.txt");}
        }

        /// <summary>
        /// Gets the license.
        /// </summary>
        /// <value>The license.</value>
        public string License
        {
            get { return ReadTextFile("License.txt"); }
        }

        #endregion

        #region constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="SAPIBot"/> class.
        /// </summary>
        /// <param name="AIMLFolderPath">The AIML folder path.</param>
        public SAPIBot(string aIMLFolderPath, string gameDataFolder, string savedGamesFolder)
        {
            _voiceBot = new VoiceBot();
            _voiceBot.Name = "Sophia";
            _voiceBot.Write += new GenericEventHandler<string>(_voiceBot_Write);
            //speech recognition code
            _recognizer = new SpeechRecognitionEngine();
            if (IsVistaOS())
            {
                _recognizer.SetInputToDefaultAudioDevice();
                _recognizer.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(recognizer_SpeechHypothesized);
                _recognizer.SpeechRecognitionRejected += new EventHandler<SpeechRecognitionRejectedEventArgs>(recognizer_SpeechRecognitionRejected);
            }
            
            //load bots
            _aimlEngine = new AIMLBotAdapter(aIMLFolderPath);
            _aimlEngine.OnUserInput += new GenericEventHandler<string>(DisplayUserInput);
            _aimlEngine.OnStart += new EventHandler(EnableSelectedGrammar);
            _aimlEngine.OnBotInfoResponse += new GenericEventHandler<IBotServer, string>(TypeVerbatim);
            _aimlEngine.OnBotResponse += new GenericEventHandler<IBotServer, string>(TypeSlow);
            _aimlEngine.OnFinish += new EventHandler<FinishEventArgs>(DisableSelectedGrammar);
            _aimlEngine.OnTextRecognized += new GenericEventHandler<string>(IBotServer_OnTextRecognized);
            _aimlEngine.OnUpdateLoadStatus += new GenericEventHandler<string>(IBotServer_OnUpdateStatus);

            GameEngineBot gameEngine = new GameEngineBot();
            gameEngine.SavedGamesFolderPath = savedGamesFolder;
            gameEngine.OnUserInput +=new GenericEventHandler<string>(DisplayUserInput);
            gameEngine.OnStart +=new EventHandler(LoadSelectedGrammar);
            gameEngine.OnBotInfoResponse += new GenericEventHandler<IBotServer, string>(TypeVerbatim);
            gameEngine.OnBotResponse += new GenericEventHandler<IBotServer, string>(TypeSlow);
            gameEngine.OnFinish += new EventHandler<FinishEventArgs>(UnloadSelectedGrammar);
            gameEngine.OnTextRecognized += new GenericEventHandler<string>(IBotServer_OnTextRecognized);
            gameEngine.OnStart += new EventHandler(gameEngine_OnStart);
            gameEngine.OnFinish += new EventHandler<FinishEventArgs>(gameEngine_OnFinish);

            _dialogEngine = new PlayGameTransitionDialogBot();
            _dialogEngine.OnBotInfoResponse += new GenericEventHandler<IBotServer, string>(TypeVerbatim);
            _dialogEngine.OnBotResponse += new GenericEventHandler<IBotServer, string>(TypeSlow);
            _dialogEngine.OnStart += new EventHandler(EnableSelectedGrammar);
            _dialogEngine.OnFinish += new EventHandler<FinishEventArgs>(DisableSelectedGrammar);
            _dialogEngine.OnLoadGame += new GenericEventHandler<string>(gameEngine.Start);
            _dialogEngine.OnTextRecognized += new GenericEventHandler<string>(IBotServer_OnTextRecognized);
            
            _firstBot = new SpeechCommandBot();
            _firstBot.GameEngineBot = gameEngine;
            _firstBot.OnBotInfoResponse += new GenericEventHandler<IBotServer, string>(TypeVerbatim);
            _firstBot.OnBotResponse += new GenericEventHandler<IBotServer, string>(TypeSlow);
            _firstBot.OnStart += new EventHandler(EnableSelectedGrammar);
            _firstBot.OnFinish += new EventHandler<FinishEventArgs>(DisableSelectedGrammar);
            _firstBot.OnChangeBackgroundColor += new GenericEventHandler<Color>(Command_OnChangeBackgroundColor);
            _firstBot.OnChangeFontColor += new GenericEventHandler<Color>(Command_OnChangeFontColor);
            _firstBot.OnTextRecognized += new GenericEventHandler<string>(IBotServer_OnTextRecognized);
            _firstBot.OnChangeFont += new GenericEventHandler<string>(Command_OnChangeFont);

            this.AppendBot(_firstBot);
            this.AppendBot(_dialogEngine);
            this.AppendBot(gameEngine);
            this.AppendBot(_aimlEngine);

            _aimlEngine.Start(aIMLFolderPath);
            _dialogEngine.Start(gameDataFolder);
            _firstBot.Start();

            _currentBot = _firstBot;
        }

        #endregion

        #region grammar manipulation methods

        /// <summary>
        /// Loads the dictation grammar.
        /// </summary>
        protected void LoadDictationGrammar()
        {
            if (!IsVistaOS())
                return;
            Thread addGrammarThread = new Thread(new ThreadStart(LoadDictation));
            addGrammarThread.IsBackground = true;
            addGrammarThread.Name = "Add Dictation Thread";
            addGrammarThread.Start();

        }

        /// <summary>
        /// Loads the dictation.
        /// </summary>
        protected void LoadDictation()
        {

            DictationGrammar dictationGrammar = new DictationGrammar();
            dictationGrammar.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(recognizer_DictationRecognized);
            lock (grammarLock)
            {
                _recognizer.UnloadAllGrammars();
                TurnSpeechRecognitionOff();
                    _recognizer.LoadGrammar(dictationGrammar);
                    BotServerBase.DebugWriteToConsole("Dictation grammar added");
                TurnSpeechRecognitionOn();
            }
            if (null != UpdateStatus)
                UpdateStatus("Dictation grammar loaded.");
            
        }

        /// <summary>
        /// Loads the context free grammars.
        /// </summary>
        protected void LoadContextFreeGrammars()
        {
            lock (grammarLock)
            {
                if (null != UpdateStatus)
                    UpdateStatus("Pause speech recognition...");
            _recognizer.UnloadAllGrammars();
            TurnSpeechRecognitionOff();
            foreach (IBotServer bot in _botServers)
            {
                if (bot.IsRunning)
                {                       
                    foreach (Grammar g in bot.GetGrammars())
                    {
                        _recognizer.LoadGrammar(g);
                        BotServerBase.DebugWriteToConsole("Grammar " + g.Name + " " + g.Priority.ToString() + " added");
                    }
                    
                }
            }
            _isGrammarInitializationComplete = true;
            TurnSpeechRecognitionOn();
            }
            if (null != UpdateStatus)
                UpdateStatus("Grammars loaded.");
        }

        /// <summary>
        /// Loads the selected grammar.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void LoadSelectedGrammar(object source, EventArgs e)
        {
            if (!IsVistaOS() || !SpecializedLexiconOn)
                return;

            Thread addGrammar = new Thread(new ParameterizedThreadStart(AddGrammar));
            addGrammar.Name = "Add Grammar Thread";
            addGrammar.IsBackground = true;
            addGrammar.Start(source);
        }

        protected void EnableSelectedGrammar(object source, EventArgs e)
        {
            if (!IsVistaOS() || !SpecializedLexiconOn)
                return;
            Thread enableGrammar = new Thread(new ParameterizedThreadStart(EnableGrammar));
            enableGrammar.Name = "Enable Grammar Thread";
            enableGrammar.IsBackground = true;
            enableGrammar.Start(source);
        }

        /// <summary>
        /// Unloads the selected grammar.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="e">The <see cref="SophiaBot.FinishEventArgs"/> instance containing the event data.</param>
        protected void UnloadSelectedGrammar(object source, FinishEventArgs e)
        {
            if (!IsVistaOS())
                return;
            Thread removeGrammar = new Thread(new ParameterizedThreadStart(RemoveGrammar));
            removeGrammar.Name = "Remove Grammar Thread";
            removeGrammar.IsBackground = true;
            removeGrammar.Start(source);
        }

        /// <summary>
        /// Disables the selected grammar.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="e">The <see cref="SophiaBot.FinishEventArgs"/> instance containing the event data.</param>
        protected void DisableSelectedGrammar(object source, FinishEventArgs e)
        {
            if (!IsVistaOS())
                return;
            Thread disableGrammar = new Thread(new ParameterizedThreadStart(DisableGrammar));
            disableGrammar.Name = "Disable Grammar Thread";
            disableGrammar.IsBackground = true;
            disableGrammar.Start(source);
        }

        /// <summary>
        /// Adds the grammar.
        /// </summary>
        /// <param name="o">The o.</param>
        protected void AddGrammar(object o)
        {
            IBotServer bot = o as IBotServer;
            if (null != bot)
            {
                BotServerBase.DebugWriteToConsole(bot.Name + " setting grammar lock (add)");
                lock (grammarLock)
                {
                    if(null != UpdateStatus)
                        UpdateStatus("Pause speech recognition...");
                    BotServerBase.DebugWriteToConsole("Pause speech recognition... " + bot.Name + " grammar lock set (add)");
                    TurnSpeechRecognitionOff();
                    foreach (Grammar g in bot.GetGrammars())
                    {
                        _recognizer.LoadGrammar(g);
                        BotServerBase.DebugWriteToConsole("Grammar " + g.Name + " " + g.Priority.ToString() + " added");                         
                    }
                    TurnSpeechRecognitionOn();
                    BotServerBase.DebugWriteToConsole(bot.Name + " release grammar lock (add)");
                }
            }
        }

        /// <summary>
        /// Removes the grammar.
        /// </summary>
        /// <param name="o">The o.</param>
        protected void RemoveGrammar(object o)
        {
            IBotServer bot = o as IBotServer;
            if (null != bot)
            {
                BotServerBase.DebugWriteToConsole(bot.Name + " setting grammar lock (remove)");
                lock (grammarLock)
                {
                    if (null != UpdateStatus)
                        UpdateStatus("Pause speech recognition...");
                    BotServerBase.DebugWriteToConsole(bot.Name + " grammar lock set (remove)");
                    for(int g =_recognizer.Grammars.Count-1; g>=0; g--)
                    {
                        TurnSpeechRecognitionOff();
                        if (_recognizer.Grammars[g].Name == bot.Name)
                        {                  
                            _recognizer.UnloadGrammar(_recognizer.Grammars[g]);
                            BotServerBase.DebugWriteToConsole("Grammar " + bot.Name + " removed");                        
                        }
                        TurnSpeechRecognitionOn();
                    }
                    BotServerBase.DebugWriteToConsole(bot.Name + " release grammar lock (remove)");
                }
            }
        }

        /// <summary>
        /// Disables the grammar.
        /// </summary>
        /// <param name="o">The o.</param>
        protected void DisableGrammar(object o)
        {
            IBotServer bot = o as IBotServer;
            if (null != bot)
            {
                for (int g = _recognizer.Grammars.Count - 1; g >= 0; g--)
                {
                    if (_recognizer.Grammars[g].Name == bot.Name)
                    {
                        _recognizer.Grammars[g].Enabled = false;
                    }
                }
            }
        }

        /// <summary>
        /// Enables the grammar.
        /// </summary>
        /// <param name="o">The o.</param>
        protected void EnableGrammar(object o)
        {
            IBotServer bot = o as IBotServer;
            if (null != bot)
            {
                for (int g = _recognizer.Grammars.Count - 1; g >= 0; g--)
                {
                    if (_recognizer.Grammars[g].Name == bot.Name)
                    {
                        _recognizer.Grammars[g].Enabled = true;
                    }
                }
            }
        }

        #endregion

        #region helper methods

        /// <summary>
        /// Reads the text file.
        /// </summary>
        /// <param name="filename">The filename.</param>
        /// <returns></returns>
        protected string ReadTextFile(string filename)
        {
            try
            {
                if (File.Exists(filename) == false)
                    return "";

                using (System.IO.FileStream fs = new System.IO.FileStream(filename, System.IO.FileMode.Open))
                {
                    byte[] textBytes = new byte[fs.Length];
                    fs.Read(textBytes, 0, (int)fs.Length);
                    return System.Text.Encoding.ASCII.GetString(textBytes);
                }
            }
            catch { return ""; }

        }


        /// <summary>
        /// Determines whether [is vista OS].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if [is vista OS]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsVistaOS()
        {
            return Environment.OSVersion.Version.Major >= 6;
        }


        /// <summary>
        /// Appends the bot.
        /// </summary>
        /// <param name="bot">The bot.</param>
        protected void AppendBot(IBotServer bot)
        {
            if (_botServers.Count > 0)
            {
                _botServers[_botServers.Count - 1].AddNextBot(bot);
                _botServers.Add(bot);
            }
            else
            {
                _botServers.Add(bot);
            }
        }


        /// <summary>
        /// _voices the bot_ write.
        /// </summary>
        /// <param name="val">The val.</param>
        protected void _voiceBot_Write(string val)
        {
            if (null != WriteToOutput)
                WriteToOutput(val);
        }

        /// <summary>
        /// Handles the OnFinish event of the gameEngine bot.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="SophiaBot.FinishEventArgs"/> instance containing the event data.</param>
        void gameEngine_OnFinish(object sender, FinishEventArgs e)
        {
            if (SpecializedLexiconOn)
            {
                _aimlEngine.Start();
                _dialogEngine.Start();
            }
        }

        /// <summary>
        /// Handles the OnStart event of the gameEngine bot.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        void gameEngine_OnStart(object sender, EventArgs e)
        {
            if (SpecializedLexiconOn)
            {
                _aimlEngine.Stop();
            }
        }

        #endregion

        #region text input/output

        /// <summary>
        /// Sends text to the client slowly, imitating a slow typist.
        /// </summary>
        /// <param name="outputText">The output text.</param>
        protected void TypeSlow(IBotServer bot, string outputText)
        {
            
            _currentBot = bot;
            //cannot recognize and synthesize at the same time
            
            lock (speechLock)
            {
                BotServerBase.DebugWriteToConsole("Begin speech Lock (slow)");
                BotServerBase.DebugWriteToConsole("Turn recognition off for slow typing");
                if (_dictationOn || _specializedLexiconOn)
                {
                    TurnSpeechRecognitionOff();
                }
                _voiceBot.TypeSlow(outputText);
                if (null != UpdateStatus)
                {
                    UpdateStatus(_currentBot.GetStatus());
                }
                if (null != InputProcessed)
                {
                    InputProcessed(this, EventArgs.Empty);
                }
                if (_dictationOn || _specializedLexiconOn)
                {
                    BotServerBase.DebugWriteToConsole("start up speech recognition after typing complete");
                    Thread restartSpeechRecognition = new Thread(new ThreadStart(SynchronizedTurnSpeechRecognitionOn));
                    restartSpeechRecognition.Name = "restart speech recognition";
                    restartSpeechRecognition.Start();
                }
                BotServerBase.DebugWriteToConsole("End speech Lock (slow)");
            }
        }

        protected void SynchronizedTurnSpeechRecognitionOn()
        {
            lock (speechLock)
            {
                BotServerBase.DebugWriteToConsole("synchronized lock free. Turn on speech recognition");
                TurnSpeechRecognitionOn();
            }
        }


        /// <summary>
        /// Types verbatim.
        /// </summary>
        /// <param name="outputText">The output text.</param>
        protected void TypeVerbatim(IBotServer bot, string outputText)
        {
            _currentBot = bot;
            lock (speechLock)
            {
                BotServerBase.DebugWriteToConsole("Begin speech Lock (verbatim)");
                BotServerBase.DebugWriteToConsole("Turn recognition off for verbatim typing");
                _voiceBot.TypeVerbatim(outputText);
                if (null != UpdateStatus)
                {
                    UpdateStatus(_currentBot.GetStatus());
                }

                if (null != InputProcessed)
                {
                    InputProcessed(this, EventArgs.Empty);
                }
                BotServerBase.DebugWriteToConsole("End speech Lock (verbatim)");
            }
        }


        /// <summary>
        /// Reads the specified input text.
        /// </summary>
        /// <param name="inputText">The input text.</param>
        public void Read(string inputText)
        {
            if (null != RecognizeText && SpeechRecognitionOn)
            {
                RecognizeText(inputText);
            }

            if (null == WriteToOutput)
                return;

            _firstBot.Read(inputText);
        }

        protected void DisplayUserInput(string inputText)
        {
            if (null != WriteToOutput)
                WriteToOutput("You: " + inputText + Environment.NewLine);
        }

        /// <summary>
        /// The bot server on update status handler.
        /// </summary>
        /// <param name="val">The val.</param>
        void IBotServer_OnUpdateStatus(string val)
        {
            if (null != UpdateStatus)
                UpdateStatus(val);
        }

        /// <summary>
        /// The bot server on text recognized handler.
        /// </summary>
        /// <param name="val">The val.</param>
        void IBotServer_OnTextRecognized(string val)
        {
            if (null != RecognizeText)
                RecognizeText(val);
        }

        /// <summary>
        /// Command_s the color of the on change font.
        /// </summary>
        /// <param name="val">The val.</param>
        void Command_OnChangeFontColor(Color val)
        {
            if (null != ChangeFontColor)
                ChangeFontColor(val);
        }

        /// <summary>
        /// Command_s the color of the on change background.
        /// </summary>
        /// <param name="val">The val.</param>
        void Command_OnChangeBackgroundColor(Color val)
        {
            if (null != ChangeBackgroundColor)
                ChangeBackgroundColor(val);
        }

        /// <summary>
        /// Command_s the on change font.
        /// </summary>
        /// <param name="val">The val.</param>
        void Command_OnChangeFont(string val)
        {
            if(null != ChangeFont)
                ChangeFont(val);
        }

        #endregion

        #region speech recognition

        /// <summary>
        /// Handles the SpeechHypothesized event of the recognizer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Speech.Recognition.SpeechHypothesizedEventArgs"/> instance containing the event data.</param>
        protected virtual void recognizer_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            if (null != GuessText)
            {
                GuessText(e.Result.Text);
            }
        }

        /// <summary>
        /// Handles the DictationRecognized event of the recognizer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Speech.Recognition.SpeechRecognizedEventArgs"/> instance containing the event data.</param>
        protected virtual void recognizer_DictationRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            string resultText = e.Result.Text;
            Read(resultText);
        }

        /// <summary>
        /// Handles the SpeechRecognitionRejected event of the recognizer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Speech.Recognition.SpeechRecognitionRejectedEventArgs"/> instance containing the event data.</param>
        protected virtual void recognizer_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            if (null != RejectText)
            {
                RejectText(e.Result.Text);
            }
            BotServerBase.DebugWriteToConsole("Rejected " + e.Result.Text);
        }

        #endregion

        #region text to speech

        /// <summary>
        /// Gets the voice list.
        /// </summary>
        /// <returns></returns>
        public string[] VoiceList
        {
            get { return _voiceBot.VoiceList; }
        }

        /// <summary>
        /// Gets or sets the voice.
        /// </summary>
        /// <value>The voice.</value>
        public string SelectedVoice
        {
            get{return _voiceBot.SelectedVoice;}
            set { _voiceBot.SelectedVoice = value; }
        }

        #endregion

        #region speech rec / text-to-speech state manipulation methods

        /// <summary>
        /// Turns text to speech on.
        /// </summary>
        protected void TurnTextToSpeechOn()
        {
            TextToSpeechOn = true;
            if (null != TextToSpeechStateChanged)
            {
                TextToSpeechStateChanged(TextToSpeechOn);
            }
        }

        /// <summary>
        /// Turns text to speech off.
        /// </summary>
        protected void TurnTextToSpeechOff()
        {
            TextToSpeechOn = false;
            if (null != _recognizer)
            {
                _recognizer.RecognizeAsyncCancel();
                TextToSpeechOn = false;
            }
            if (null != TextToSpeechStateChanged)
            {
                TextToSpeechStateChanged(TextToSpeechOn);
            }
        }

        /// <summary>
        /// Turns speech recognition on.
        /// </summary>
        protected void TurnSpeechRecognitionOn()
        {
            lock (speechRecOnOffLock)
            {
                if (1 <= Interlocked.Increment(ref speechRecOnOffState))
                {
                    //don't allow to exceed 1
                    Interlocked.Exchange(ref speechRecOnOffState, 1);
                    Console.WriteLine("{0} released the interlock. interlock state: {1}", Thread.CurrentThread.Name,Interlocked.Read(ref speechRecOnOffState));
                    if (null != _recognizer)
                    {
                        if (!this.SpeechRecognitionOn)
                        {
                            if (_recognizer.Grammars.Count > 0 && (_dictationOn || (_isGrammarInitializationComplete && _specializedLexiconOn)))
                            {
                                try
                                {
                                    BotServerBase.DebugWriteToConsole("Try to set input device");
                                    _recognizer.SetInputToDefaultAudioDevice();
                                }
                                catch (Exception ex)
                                {
                                    BotServerBase.DebugWriteToConsole(ex.Message);
                                    if (ex.InnerException != null)
                                        BotServerBase.DebugWriteToConsole("inner exception: " + ex.InnerException.Message);
                                }
                                try
                                {
                                    _recognizer.RecognizeAsync(RecognizeMode.Multiple);
                                    BotServerBase.DebugWriteToConsole("speech recognition turned on " + ++recCount);
                                    SpeechRecognitionOn = true;
                                    if (null != UpdateStatus)
                                        UpdateStatus("Speech recognition on...");
                                }
                                catch (Exception ex)
                                {
                                    BotServerBase.DebugWriteToConsole(ex.Message);
                                    if (ex.InnerException != null)
                                        BotServerBase.DebugWriteToConsole("inner exception: " + ex.InnerException.Message);
                                }
                            }
                        }
                    }
                }
                else
                {
                    Console.WriteLine("      {0} could not release the interlock: interlock state:{1}", Thread.CurrentThread.Name, Interlocked.Read(ref speechRecOnOffState));
                }
            }
        }

        /// <summary>
        /// Turns speech recognition off.
        /// </summary>
        protected void TurnSpeechRecognitionOff()
        {
            lock (speechRecOnOffLock)
            {
                if (0 == Interlocked.Decrement(ref speechRecOnOffState))
                {
                    Console.WriteLine("{0} acquired the interlock. interlock state: {1}", Thread.CurrentThread.Name, Interlocked.Read(ref speechRecOnOffState));
                    if (null != _recognizer)
                    {
                        if (this.SpeechRecognitionOn)
                        {
                            _recognizer.RecognizeAsyncCancel();
                            SpeechRecognitionOn = false;
                            BotServerBase.DebugWriteToConsole("speech recognition turned off " + --recCount);
                        }
                    }
                }
                else
                {
                    Console.WriteLine("      {0} was denied the interlock: interlock state:{1}", Thread.CurrentThread.Name, Interlocked.Read(ref speechRecOnOffState));
                }
            }
        }

        /// <summary>
        /// Turns dictation on.
        /// </summary>
        protected void TurnDictationOn()
        {
            DictationOn = true;
            LoadDictationGrammar();
            if (null != DictationStateChanged)
            {
                DictationStateChanged(DictationOn);
            }
        }

        /// <summary>
        /// Turns dictation off.
        /// </summary>
        protected void TurnDictationOff()
        {
            DictationOn = false;
            if (null != DictationStateChanged)
            {
                DictationStateChanged(DictationOn);
            }
        }

        /// <summary>
        /// Turns the specialized lexicon on.
        /// </summary>
        protected void TurnSpecializedLexiconOn()
        {
            SpecializedLexiconOn = true;
            //load active grammars
            Thread loadGrammarsThread = new Thread(new ThreadStart(LoadContextFreeGrammars));
            loadGrammarsThread.Name = "Load Context Free Grammars Thread";
            loadGrammarsThread.IsBackground = true;
            loadGrammarsThread.Start();

            if (null != SpecializedLexiconStateChanged)
            {
                SpecializedLexiconStateChanged(SpecializedLexiconOn);
            }
        }

        /// <summary>
        /// Turns the specialized lexicon off.
        /// </summary>
        protected void TurnSpecializedLexiconOff()
        {
            SpecializedLexiconOn = false;
            if (null != SpecializedLexiconStateChanged)
            {
                SpecializedLexiconStateChanged(SpecializedLexiconOn);
            }
        }

        /// <summary>
        /// Toggles the dictation.
        /// </summary>
        public void ToggleDictation()
        {
            //thread to prevent deadlock 
            //between UI client and Sophiabot
            Thread tog = new Thread(new ThreadStart(toggleDictation));
            tog.Name = "toggle dictation";
            tog.Start();
        }

        private void toggleDictation()
        {
            lock (speechLock)
            {
                if (DictationOn)
                {
                    BotServerBase.DebugWriteToConsole("Dictation toggled off");
                    TurnDictationOff();
                    TurnSpeechRecognitionOff();
                    _recognizer.UnloadAllGrammars();         
                }
                else
                {   
                    BotServerBase.DebugWriteToConsole("Dictation toggled on");       
                    TurnSpecializedLexiconOff();
                    TurnDictationOn();
                    TurnSpeechRecognitionOn();
                }
            }
        }

        /// <summary>
        /// Toggles the specialized lexicon.
        /// </summary>
        public void ToggleSpecializedLexicon()
        {
            //thread to prevent deadlock 
            //between UI client and Sophiabot
            Thread tog = new Thread(new ThreadStart(toggleSpecializedLexicon));
            tog.Name = "toggle special lexicons";
            tog.Start();
        }

        protected void toggleSpecializedLexicon()
        {
            lock (speechLock)
            {
                if (SpecializedLexiconOn)
                {
                    BotServerBase.DebugWriteToConsole("special lexicon toggled off");
                    TurnSpeechRecognitionOff();
                    TurnSpecializedLexiconOff();
                    _recognizer.UnloadAllGrammars();
                }
                else
                {
                    BotServerBase.DebugWriteToConsole("special lexicon toggled on");
                    TurnDictationOff();
                    TurnSpecializedLexiconOn();
                    TurnSpeechRecognitionOn();
                }
            }
        }



        /// <summary>
        /// Toggles text to speech.
        /// </summary>
        public void ToggleTextToSpeech()
        {
            //thread to prevent deadlock 
            //between UI client and Sophiabot
            Thread tog = new Thread(new ThreadStart(toggleTextToSpeech));
            tog.Name = "toggle text to speech";
            tog.Start();
        }

        protected void toggleTextToSpeech()
        {
            lock (speechLock)
            {
                if (TextToSpeechOn)
                {
                    TurnTextToSpeechOff();
                }
                else
                {
                    TurnTextToSpeechOn();
                }
            }
        }

        #endregion

    }
}
